'''
''' IBAN Functions - IBANAnalyzer.vb
''' 
''' This program is free software; you can redistribute it and/or modify
''' it under the terms of the GNU General Public License as published by
''' the Free Software Foundation; either version 2 of the License, or
''' (at your option) any later version.
'''
''' This program is distributed in the hope that it will be useful,
''' but WITHOUT ANY WARRANTY; without even the implied warranty of
''' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
''' GNU General Public License for more details.
'''
''' You should have received a copy of the GNU General Public License
''' along with this program; if not, write to the Free Software
''' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'''

''' -----------------------------------------------------------------------------
''' Project	 : IBANFuncsCore
''' Class	 : Core.IBANAnalyzer
''' 
''' -----------------------------------------------------------------------------
''' <summary>
''' This class implements methods to analyze an IBAN to its elements.
''' </summary>
''' <remarks>
''' The class shared (static) methods that analyze and validate an IBAN.
''' </remarks>
''' <history>
''' 	[Nick]	2/8/2007	Created
''' </history>
''' -----------------------------------------------------------------------------
Public Class IBANAnalyzer

#Region "Public methods"

    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Analyzes an IBAN to its elements.
    ''' </summary>
    ''' <param name="IBANStr">A string representing the IBAN to be analyzed.</param>
    ''' <returns>An instance of <see cref="IBANContents"/> with the analysis results.</returns>
    ''' <remarks>
    ''' This method also calls the <see cref="M:IBANFuncs.Core.IBANAnalyzer.ValidateContents"/> method.
    ''' In addition to the exceptions thrown by this method, you should be prepared to catch any
    ''' exception raised by <see cref="M:IBANFuncs.Core.IBANAnalyzer.ValidateContents"/>.
    ''' </remarks>
    ''' <exception cref="XIBANInvalidStringException">Thrown when a null string is passed for analysis.</exception>
    ''' <exception cref="XIBANInvalidStringException">Thrown when an IBAN string is either too large or too small.</exception>
    ''' <exception cref="XIBANInvalidCountryCodeException">Thrown when an IBAN string contains an invalid country code.</exception>
    ''' <exception cref="XIBANInvalidStringException">Thrown when an IBAN string has an invalid length for the specific country.</exception>
    ''' <exception cref="XIBANIncorrectCheckDigitsException">Throw when an IBAN is correctly analyzed but has incorrect check digits.</exception>
    ''' <history>
    ''' 	[Nick]	2/8/2007	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Shared Function AnalyzeIBAN(ByVal IBANStr As String) As IBANContents

        'No nulls
        If IBANStr Is Nothing Then Throw New XIBANInvalidStringException("IBAN string is null")

        'No short/long strings
        If IBANStr.Length > IBANDefinitions.MaxIBANLength Then Throw New XIBANInvalidStringException("IBAN string too long")
        If IBANStr.Length < IBANDefinitions.MinIBANLength Then Throw New XIBANInvalidStringException("IBAN string too short")

        Dim countryCode As String = "", checkDigits As String = "", _
            bankCode As String = "", branchCode As String = "", accNbr As String = ""

        'Get the country code
        countryCode = IBANStr.Substring(0, 2)

        'Find the country
        Dim cntr As IBANCountryDef = IBANDefinitions.FindCountryByCode(countryCode)
        If cntr Is Nothing Then Throw New XIBANInvalidCountryCodeException("Invalid country code " + countryCode)

        'Check specific length
        If cntr.IBANLength <> IBANStr.Length Then Throw New XIBANInvalidStringException("Incorrect length for country " + countryCode)

        'Parse elements
        Dim idx As Integer

        checkDigits = IBANStr.Substring(2, 2)

        idx = 4

        'Parse bank code
        If cntr.BankCodeLength <> 0 Then
            bankCode = IBANStr.Substring(idx, cntr.BankCodeLength)
            idx += cntr.BankCodeLength
        End If

        'Parse branch code
        If cntr.BranchCodeLength <> 0 Then
            branchCode = IBANStr.Substring(idx, cntr.BranchCodeLength)
            idx += cntr.BranchCodeLength
        End If

        'Parse account number
        If cntr.AccountNumberLength <> 0 Then
            accNbr = IBANStr.Substring(idx, cntr.AccountNumberLength)
        End If

        ValidateContents(bankCode, branchCode, accNbr, cntr)

        'Check the IBAN check digits
        If IBANAlgorithm.CreateIBAN(countryCode, bankCode, branchCode, accNbr) <> _
           IBANStr Then Throw New XIBANIncorrectCheckDigitsException("Incorrect check digits")

        'Parsed and correct
        Return New IBANContents(countryCode, checkDigits, bankCode, branchCode, accNbr)

    End Function

    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' Performs validation on IBAN elements.
    ''' </summary>
    ''' <param name="bankCode">IBAN bank code.</param>
    ''' <param name="branchCode">IBAN branch code.</param>
    ''' <param name="accountNumber">IBAN account number.</param>
    ''' <param name="Country">Instance of <see cref="IBANCountryDef"/> with country definition.</param>
    ''' <remarks>
    ''' </remarks>
    ''' <exception cref="XIBANNotNumericException">Thrown when the bank or branch code or the account number 
    ''' contains alphanumeric characters but shouldn't.</exception>
    ''' <exception cref="XIBANNumericCharacterException">Throw when the bank, branch or account number should 
    ''' contain an alphanumeric character at a specific position but doesn't.</exception>
    ''' <history>
    ''' 	[Nick]	2/8/2007	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Shared Sub ValidateContents(ByVal bankCode As String, _
                                       ByVal branchCode As String, _
                                       ByVal accountNumber As String, _
                                       ByVal Country As IBANCountryDef)

        'Check bank code numeric chars
        If Country.BankCodeLength > 0 AndAlso Country.BankCodeAlpha = False AndAlso IsNumeric(bankCode) = False Then _
                    Throw New XIBANNotNumericException("Bank code is not numeric")

        'Check branch code numeric chars
        If Country.BranchCodeLength > 0 AndAlso Country.BranchCodeAlpha = False AndAlso IsNumeric(branchCode) = False Then _
                            Throw New XIBANNotNumericException("Branch code is not numeric")

        'Check account number numeric chars
        If Country.AccountNumberLength > 0 AndAlso Country.AccountNumberAlpha = False AndAlso IsNumeric(accountNumber) = False Then _
                            Throw New XIBANNotNumericException("Account number is not numeric")

        'Validate specific alphanumeric chars
        Dim IBANStr As String = "XXXX" + bankCode + branchCode + accountNumber
        If Country.CharPositionsNum >= 0 Then
            For i As Integer = 0 To Country.CharPositions().GetUpperBound(0)
                If Char.IsDigit(IBANStr, Country.CharPositions(i) - 1) = True Then
                    Throw New XIBANNumericCharacterException("Expected alphanumeric character, found numeric at position " + Country.CharPositions(i).ToString)
                End If
            Next
        End If

    End Sub

#End Region

End Class
